import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;

/**
 * DataGenerator class is responsible for creating test context data
 * for the optimizer. 
 * 
 * @author Yong Shin
 *
 */
public class DataGenerator {

	/** Comments start with this character */
	private static final char COMMENT = '#';
	
	/** Number of data to generate */
	private int numData;
	
	/** Number of contexts in context definition */
	private int numCxt;
	
	/** Random number generator */
	private Random random;
	
	/** Contains data generation specifications */
	private ArrayList<int[]> specs;
	
	private DataGenerator(String ccdFile) {
		ContextDefinition.setCCDFile(ccdFile);
		specs = new ArrayList<int[]>();
		random = new Random();
		numCxt = ContextDefinition.getContextDefinition().getNumContexts();
	}
	
	private void loadDGSFile(String dgsFile) {
		
		// read data generation specification file and parse it
		BufferedReader bufread = null;
		try {
			bufread = new BufferedReader(new FileReader(dgsFile));
			String inputLine;

			// read number of inputs to generate
			while ( (inputLine = bufread.readLine()) != null ) {
				
				// trim leading and trailing white spaces
				inputLine.trim();
				
				// skip empty or commented lines
				if (inputLine.length() != 0 && inputLine.indexOf(COMMENT) != 0)
					break;
			}
			
			// parse number of data to generate
			numData = Integer.parseInt(inputLine);

			// read rest of data
			while ( (inputLine = bufread.readLine()) != null ) {
				
				// trim leading and trailing white spaces
				inputLine.trim();
				
				// skip empty or commented lines
				if (inputLine.length() == 0 || inputLine.indexOf(COMMENT) == 0)
					continue;

				// create an array to store data generation specification
				int[] spec = new int[numCxt+2];
				
				// input items are separated by commas
				Scanner scan = new Scanner(inputLine);
				scan.useDelimiter("\\s*\\,\\s*");
				
				// read bucket indexes for each context
				for (int i=0; i<numCxt; i++) {
					// bucket id is zero-based, where as the bucket id in dgs file
					// is one-based
					spec[i] = scan.nextInt()-1;
				}
				
				// read distribution and bit bias
				for (int i=numCxt; i<numCxt+2; i++) {
					spec[i] = scan.nextInt();
				}
				
				// add specification
				specs.add(spec);
			}
			
			// verify that the percentages add up to 100
			int sum = 0;
			for (int i=0; i<specs.size(); i++) {
				sum += specs.get(i)[numCxt];
			}
			if (sum != 100) 
				throw new RuntimeException("Distribution does not add up to 100.");
			
			// report progress
			System.out.println("finished parsing dgs file...");
			
		} catch (IOException ex) {
			System.out.println(ex.getMessage());
		} finally {
			if (bufread != null) try {bufread.close(); } catch (Exception ex){}
		}	
	}
	
	private String randomData() {
		StringBuilder sb = new StringBuilder();

		// generate a random number between 0 (inclusive) and 100, then find the specification
		// corresponding to the accumulated percent
		int percent = random.nextInt(100);
		int idx = 0;
		while (percent >= specs.get(idx)[numCxt]) {
			percent -= specs.get(idx)[numCxt];
			idx++;
		}
		int[] spec = specs.get(idx);
		
		// generate data fitting the selected specification
		ContextDefinition ctxdef = ContextDefinition.getContextDefinition();
		
		// bit bias
		int bitBias = random.nextInt(100);
		if (bitBias < spec[numCxt+1])	// spec[numCxt+1] gives one-bit bias
			sb.append(1 + " ");
		else
			sb.append(0 + " ");
		
		// add context values corresponding to bucket indices in the specification
		for (int i=0; i<numCxt; i++) {
			Context cxt = ctxdef.getContext(i);
			if (spec[i] == -1) {
				// this indicates that we can choose any random value for this context
				int bucketID = random.nextInt(cxt.getNumberBuckets());
				sb.append(cxt.getContextForBucketID(bucketID) + " ");
			} else {
				sb.append(cxt.getContextForBucketID(spec[i]) + " ");
			}
		}
		
		return sb.toString();
	}
	
	private void generateData(String ccFile) {

		BufferedWriter bufwrite = null;
		try {
			bufwrite = new BufferedWriter(new FileWriter(ccFile));
			
			for (int i=0; i<numData; i++) {
				bufwrite.write(randomData() + "\n");
			}
			
			bufwrite.flush();
			
			// report progress
			System.out.println("finished writing " + numData + " data lines...");
			
		} catch (IOException ex) {
			System.out.println(ex.getMessage());
		} finally {
			if (bufwrite != null) try {bufwrite.close(); } catch (Exception ex){}
		}			
	}
	
	public static void main(String args[]) {
		DataGenerator dg = new DataGenerator("default.ccd");
		dg.loadDGSFile("default.dgs");
		dg.generateData("default.cc");
	}
}
